﻿using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace ATEGo
{
    /// <summary>
    /// 运行Windows命令处理程序
    /// </summary>
    public class WindowsCmd
    {
        /// <summary>
        /// 执行后输出的事件
        /// </summary>
        public event Action<string> OutputMethod;
        /// <summary>
        /// 输入CMD命令事件
        /// </summary>
        public event Func<string> InputMethod;
        /// <summary>
        /// 命令结束后调用事件
        /// </summary>
        public event Action EndMethod;

        /// <summary>
        /// 每次命令发送完毕后，回显的最后是否有新的命令提示符
        /// </summary>
        public bool IsExcutedPrompt { get; set; } = true;
        /// <summary>
        /// 设置命令的换行符。
        /// </summary>
        public string NewLine
        {
            get
            {
                return mycmd.StandardInput.NewLine;
            }
            set
            {
                mycmd.StandardInput.NewLine = value;
            }
        }

        Process mycmd = new Process();
        /// <summary>
        /// 指示是否执行成功
        /// </summary>
        bool temp = false;
        public WindowsCmd()
        {
            ProcessStartInfo startInfo = new ProcessStartInfo
            {
                FileName = "cmd.exe",
                Arguments = $"",
                //是否使用操作系统shell启动
                //要重定向 IO 流，Process 对象必须将 UseShellExecute 属性设置为 False。
                UseShellExecute = false,
                //接受来自调用程序的输入信息
                RedirectStandardInput = true,
                //由调用程序获取输出信息
                RedirectStandardOutput = true,
                //重定向标准错误输出
                RedirectStandardError = true,
                //是否不创建窗口，UseShellExecute必须设置成true
                CreateNoWindow = true,
                //启动时工作目录
                WorkingDirectory = ""
            };
            mycmd.StartInfo = startInfo;
            
            // 当写入输出流时 触发该事件
            mycmd.OutputDataReceived += (sender, e) => {
                if (e.Data!=null)
                {
                    OutputMethod?.Invoke(e.Data); //将数据输出
                }               
                if (!string.IsNullOrEmpty(e.Data))
                {
                    temp = true; //执行成功
                }
            };
            // 当发生错误时写入输出流时 触发该事件
            mycmd.ErrorDataReceived+= (sender, e) => {
                if (e.Data != null)
                {
                    OutputMethod?.Invoke(e.Data); //将数据输出
                }
                if (!string.IsNullOrEmpty(e.Data))
                {
                    temp = false; //执行失败
                }
            };
            mycmd.Start(); //开始执行程序
            mycmd.BeginOutputReadLine(); //开始异步读取操作
            mycmd.BeginErrorReadLine(); //开始异步读取错误输出
            mycmd.StandardInput.AutoFlush = true;
        }
        /// <summary>
        /// 执行指定的命令，调用InputMethod事件
        /// </summary>
        public void Excute()
        {
            if (!mycmd.HasExited)
            {
                mycmd.StandardInput.WriteLine(InputMethod?.Invoke());
            }
            if (IsExcutedPrompt)
            {
                //必须要多写入一个换行符，不然最后一个命令提示符出不来
                mycmd.StandardInput.WriteLine("");
            }
        }
        /// <summary>
        /// 执行指定的命令
        /// </summary>
        /// <param name="cmd">cmd命令</param>
        /// <returns></returns>
        public void Excute(string cmd)
        {
            if (!mycmd.HasExited)
            {
                mycmd.StandardInput.WriteLine(cmd);
            }
            if (IsExcutedPrompt)
            {
                //必须要多写入一个换行符，不然最后一个命令提示符出不来
                mycmd.StandardInput.WriteLine("");
            }
        }
        /// <summary>
        /// 结束cmd执行。有问题？
        /// 会返回最后一条命令是否执行成功
        /// </summary>
        /// <returns>返回是否执行成功，如果有多条执行 只会返回最后一次是否指向成功</returns>
        public bool Close()
        {
            mycmd.StandardInput.WriteLine("exit");
            // 如果5秒之内没结束 也会主动结束。
            
            mycmd.WaitForExit(5000); //等待结束。否则Excute后执行close会立即结束来不及得到输出流。
            mycmd.CancelOutputRead(); //结束异步读取操作
            mycmd.CancelErrorRead();
            mycmd.Close();
            return temp;
        }

        public async void WaitToEnd()
        {
            await Task.Run(() =>
            {
                mycmd.WaitForExit();
                EndMethod?.Invoke();
            });
        }

    }

}
